package utils

import (
	"bytes"
	"encoding/gob"
	"fmt"
	"reflect"
	"strings"
	"time"
)

//用gob进行数据序列化
func Serialize(data interface{}) ([]byte, error) {
	buf := bytes.NewBuffer(nil)
	enc := gob.NewEncoder(buf)
	err := enc.Encode(data)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

// 用gob进行数据反序列化
func Deserialize(data []byte, to interface{}) error {
	buf := bytes.NewBuffer(data)
	dec := gob.NewDecoder(buf)
	return dec.Decode(to)
}

//slice interface 变数组
func ToSlice(arr interface{}) []interface{} {
	v := reflect.ValueOf(arr)
	if v.Kind() != reflect.Slice {
		return []interface{}{}
	}
	l := v.Len()
	ret := make([]interface{}, l)
	for i := 0; i < l; i++ {
		ret[i] = v.Index(i).Interface()
	}
	return ret
}

//强大的join
func Join(arr interface{},sep string) string{
	res:=ToSlice(arr)

	str:=""
	for _,v:=range res {
		if tmpStr,ok:=v.(string);ok{
			str+=tmpStr+sep
		}
	}

	return strings.TrimRight(str,sep)
}


func InArray(item interface{},array interface{}) bool {
	if reflect.TypeOf(array).Kind() != reflect.Slice {
		return false
	}

	n := reflect.ValueOf(array).Len()
	for i := 0; i < n; i++ {
		if reflect.ValueOf(array).Index(i).Interface() == reflect.ValueOf(item).Interface() {
			return true
		}
	}

	return false

}

//去空,传了field 只处理field里面的字段
func TrimStruct(obj interface{}, field ...string) {

	objT, objV, _ := GetStructTV(obj)

	//不是指针不进行处理
	if !IsStructPtr(reflect.TypeOf(obj)) {
		return
	}

	for i := 0; i < objT.NumField(); i++ {

		if objT.Field(i).Type.Kind() == reflect.String {

			if len(field) == 0 || InArray(objT.Field(i).Name, field) {
				//替换值
				value := objV.Field(i)
				value.SetString(strings.Trim(value.String(), " "))
			}

		}

		if objT.Field(i).Type.Kind() == reflect.Ptr && !objV.Field(i).IsNil() {
			TrimStruct(objV.Field(i).Interface(), field...)
		}
	}
}


//判定是否为结构体
func IsStruct(t reflect.Type) bool {
	return t.Kind() == reflect.Struct
}

//判定是否为结构体指针
func IsStructPtr(t reflect.Type) bool {
	return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
}

//获取结构体或者指针的类型和值
func GetStructTV(obj interface{}) (reflect.Type, reflect.Value, error) {
	objT := reflect.TypeOf(obj)
	objV := reflect.ValueOf(obj)

	switch {
	case IsStruct(objT):
	case IsStructPtr(objT):
		objT = objT.Elem()
		objV = objV.Elem()
	default:
		return objT, objV, fmt.Errorf("%v must be a struct or a struct pointer", obj)
	}

	return objT, objV, nil
}


// IsSatisfied judge whether obj has value
func IsSatisfied(obj interface{}) bool {
	if obj == nil {
		return false
	}

	if str, ok := obj.(string); ok {
		return len(strings.TrimSpace(str)) > 0
	}
	if _, ok := obj.(bool); ok {
		return true
	}
	if i, ok := obj.(int); ok {
		return i != 0
	}
	if i, ok := obj.(uint); ok {
		return i != 0
	}
	if i, ok := obj.(int8); ok {
		return i != 0
	}
	if i, ok := obj.(uint8); ok {
		return i != 0
	}
	if i, ok := obj.(int16); ok {
		return i != 0
	}
	if i, ok := obj.(uint16); ok {
		return i != 0
	}
	if i, ok := obj.(uint32); ok {
		return i != 0
	}
	if i, ok := obj.(int32); ok {
		return i != 0
	}
	if i, ok := obj.(int64); ok {
		return i != 0
	}
	if i, ok := obj.(uint64); ok {
		return i != 0
	}
	if t, ok := obj.(time.Time); ok {
		return !t.IsZero()
	}
	v := reflect.ValueOf(obj)
	if v.Kind() == reflect.Slice {
		return v.Len() > 0
	}
	return true
}


//获取结构体的非零值字段
func GetNotZeroFields(obj interface{}, fields ...string) []string {
	objT := reflect.TypeOf(obj)
	objV := reflect.ValueOf(obj)

	switch {
	case IsStruct(objT):
	case IsStructPtr(objT):
		objT = objT.Elem()
		objV = objV.Elem()
	default:
		return fields
	}
	for i := 0; i < objT.NumField(); i++ {
		//字段名称
		currentField := objT.Field(i).Name

		//字段值
		currentFieldValue := objV.Field(i).Interface()
		if objV.Field(i).Kind() == reflect.Ptr {
			if objV.Field(i).IsNil() {
				currentFieldValue = ""
			} else {
				currentFieldValue = objV.Field(i).Elem().Interface()
			}
		}
		haveValue := IsSatisfied(currentFieldValue)

		if haveValue && !InArray(currentField, fields) {
			fields = append(fields, currentField)
		}

	}
	return fields
}